
#include <ecal/ecal.h>

#include <chrono>
#include <iostream>
#include <thread>

#include "argparse/argparse.hpp"
#include "binary_payload_writer.h"

// warmup runs not to measure
const int warmups(100);

// single test run
void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) {
  // log parameter
  std::cout << "--------------------------------------------" << std::endl;
  std::cout << "Runs                    : " << runs << std::endl;
  std::cout << "Message size            : " << snd_size << " kB" << std::endl;
  std::cout << "Memory buffer           : " << mem_buffer << std::endl;
  if (zero_copy) {
    std::cout << "Zero copy               : ON" << std::endl;
  } else {
    std::cout << "Zero copy               : OFF" << std::endl;
  }

  // initialize eCAL API
  eCAL::Initialize(0, nullptr, "latency_snd");

  // create publisher and subscriber
  eCAL::CPublisher pub("ping");

  // set number of publisher memory buffers
  pub.ShmSetBufferCount(mem_buffer);

  // enable zero copy mode
  pub.ShmEnableZeroCopy(zero_copy);

  // prepare send buffer
  CBinaryPayload payload(snd_size * 1024);

  // let them match
  std::this_thread::sleep_for(std::chrono::milliseconds(2000));

  // add some extra loops for warmup :-)
  int run(0);
  for (; run < runs + warmups; ++run) {
    // get microseconds
    auto snd_time =
        std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
            .count();
    // send message (with receive timeout 100 ms)
    pub.Send(payload, snd_time, 100 /*ms*/);
  }

  // log test
  std::cout << "Messages sent           : " << run - warmups << std::endl;
  std::cout << "--------------------------------------------" << std::endl;

  // let the receiver do the evaluation
  std::this_thread::sleep_for(std::chrono::milliseconds(2000));

  // finalize eCAL API
  eCAL::Finalize();
}

int main(int argc, char** argv) {
  try {
    // parse command line
    argparse::ArgumentParser program("latency_snd");
    program.add_argument("-r", "--runs").default_value(5000).help("Number of messages to send.").scan<'i', int>();
    program.add_argument("-s", "--size")
        .default_value(-1)
        .help("Messages size in kB. If -1, automatic size mode is used.")
        .scan<'i', int>();
    program.add_argument("-b", "--mem_buffer")
        .default_value(1)
        .help("Number of memory files per connection.")
        .scan<'i', int>();
    program.add_argument("-z", "--zero_copy")
        .default_value(false)
        .implicit_value(true)
        .help("Switch zero copy mode on.");
    program.parse_args(argc, argv);
    int runs = program.get<int>("runs");
    int size = program.get<int>("size");
    int mem_buffer = program.get<int>("mem_buffer");
    bool zero_copy = program.get<bool>("zero_copy");

    if (size < 0) {
      // automatic size mode
      for (int s = 1; s <= 32768; s *= 2) do_run(runs, s, mem_buffer, zero_copy);
    } else {
      // run single test
      do_run(runs, size, mem_buffer, zero_copy);
    }
  } catch (const std::exception& ex)  // catch any exceptions
  {
    std::cerr << "error: " << ex.what() << " for arg." << std::endl;
    return -1;
  }

  return 0;
}
